﻿using DrawTools.Serialize;
using DrawTools.Tools;
using DrawTools.Utils;
using LS.Screening.UI.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using DashStyle = System.Windows.Media.DashStyle;

namespace DrawTools
{
    /// <summary>
    /// </summary>
    public abstract class DrawGeometryBase : DrawingVisual, IDrawTool
    {
        public DrawGeometryBase(DrawingCanvas drawingCanvas)
        {
            this.drawingCanvas = drawingCanvas;
        }

        #region 鼠标键盘事件

        public virtual Boolean OnKeyDown(Key key) => false;

        public virtual Boolean OnKeyUp(Key key) => false;

        public virtual Boolean OnTouchDown(Int32 touchId, Point point) => false;

        public virtual Boolean OnTouchEnter(Point point) => false;

        public virtual Boolean OnTouchLeave(Point point, bool isLeaved) => false;

        public virtual Boolean OnTouchMove(Point point) => false;

        public virtual Boolean OnTouchUp(Point point) => false;

        #endregion 鼠标键盘事件

        #region 绘图事件

        protected virtual void OnDrawing(DrawingContext dc)
        {
        }

        /// <summary>
        /// 绘图
        /// </summary>
        /// <param name="beforeDrawAction">在绘制最后增加的代码</param>
        public virtual void Draw()
        {
            var dc = this.RenderOpen();
            OnDrawing(dc);
            dc.DrawGeometry(null, Pen, geometry);
            DrawIndex(dc);
            dc.Close();
            if (!string.IsNullOrEmpty(this.Text) && this.Index > 0 && this.ShowIndex)
            {
                drawingCanvas.AddTogetherText(this.Index.ToString().PadLeft(2) + ")---- " + this.Text);
            }
            //drawingCanvas.RefreshTogetherText();
        }

        private static List<Type> canDeletePointTypes = new List<Type>
        {
            typeof(AreaDrawTool),
            typeof(PenDrawTool),
            typeof(PolygonDrawTool),
            typeof(PolylineDrawTool),
            typeof(CurveDrawTool),
            typeof(TextDrawTool),
            typeof(PixelRateDrawTool),
            typeof(AreaClosedLineDrawTool),
            typeof(ClosedLineDrawTool)
        };

        public virtual Point? DeleteLastPoint()
        {
            if (!canDeletePointTypes.Contains(this.GetType())) return null;
            if (this.pathGeometry != null && this.pathGeometry.IsFrozen == false && this.pathGeometry.Figures.Count > 0)
            {
                var figure = this.pathGeometry.Figures[this.pathGeometry.Figures.Count - 1];
                if (figure.Segments.Count > 0 && figure.Segments.IsFrozen == false)
                {
                    figure.Segments.RemoveAt(figure.Segments.Count - 1);
                    if (figure.Segments.Count > 0)
                    {
                        var segment = figure.Segments[figure.Segments.Count - 1];
                        if (segment is PolyLineSegment ps)
                        {
                            return ps.Points[ps.Points.Count - 1];
                        }
                        if (segment is BezierSegment bs)
                        {
                            return bs.Point2;
                        }
                        if (segment is LineSegment ls)
                        {
                            return ls.Point;
                        }
                        if (segment is ArcSegment asg)
                        {
                            return asg.Point;
                        }
                    }
                }
            }
            return null;
        }

        public virtual Boolean Erase(Geometry erase)
        {
            geometry = Geometry.Combine(geometry, erase, GeometryCombineMode.Exclude, null);
            if (geometry.IsEmpty()) return true;
            Draw();
            return false;
        }

        public virtual Boolean Select(Point point)
        {
            return geometry.FillContains(point);
        }

        public virtual Boolean Select(Geometry select)
        {
            return !Geometry.Combine(geometry, select, GeometryCombineMode.Intersect, null).IsEmpty();
        }

        public virtual Rect Selected()
        {
            if (Mode == 1) return selectRect;
            Mode = 1;
            var dc = this.RenderOpen();
            dc.DrawGeometry(Pen.Brush, null, geometry);
            selectRect = GetRenderBounds();
            dc.DrawRectangle(Brushes.Transparent, this.drawingCanvas.SelectBackgroundPen, selectRect);
            dc.DrawRectangle(null, this.drawingCanvas.SelectPen, selectRect);
            dc.Close();
            return selectRect;
        }

        public virtual void Unselected()
        {
            if (Mode == 0) return;
            Mode = 0;
            Draw();
        }

        protected virtual Rect GetRenderBounds()
        {
            return geometry.GetRenderBounds(this.drawingCanvas.SelectPen);
        }

        public virtual void Move(Double dx, Double dy)
        {
            if ((geometry.Transform as TranslateTransform) == null)
            {
                geometry.Transform = new TranslateTransform(dx, dy);
            }
            else
            {
                var translate = (TranslateTransform)geometry.Transform;
                translate.X += dx;
                translate.Y += dy;
            }
            if (Mode == 1)
            {
                Mode = 0;
                Selected();
            }
            else Draw();
        }

        public virtual void Edit()
        { }

        #endregion 绘图事件

        #region 序列化

        public virtual DrawGeometrySerializerBase ToSerializer()
        {
            var att = this.GetType().GetCustomAttribute<SerializerTypeAttribute>();
            if (att == null) return null;

            var serializer = (DrawGeometrySerializerBase)att.Type.GetConstructor(Type.EmptyTypes).Invoke(null);
            serializer.Color = ((SolidColorBrush)Pen.Brush).Color;
            serializer.StrokeThickness = Pen.Thickness;
            serializer.Geometry = geometry.ToString();
            serializer.Index = Index;
            serializer.ShowIndex = ShowIndex;
            serializer.Text = Text;
            serializer.BeginingPoint = BeginingPoint;
            if (geometry.Transform != null)
            {
                serializer.Matrix = geometry.Transform.Value;
            }
            var serializerClassDescribe = ClassDescribe.Get(att.Type);
            var serializerProperties = serializerClassDescribe.PropertyDescribes.Where(p => p.PropertyInfo.DeclaringType == att.Type).Select(p => p.PropertyInfo).ToList();
            foreach (var pro in serializerProperties)
            {
                var p = this.GetType().GetProperty(pro.Name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);

                if (p != null && p.CanWrite)
                {
                    var val = p.GetValue(this);
                    pro.SetValue(serializer, val);
                }
                else
                {
                    var fieldName = pro.Name[0].ToString().ToLower() + pro.Name.Substring(1);
                    var flag = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic;
                    var f = this.GetType().GetField(fieldName, flag);
                    if (f == null)
                    {
                        f = this.GetType().GetField("_" + fieldName, flag);
                    }
                    if (f != null)
                    {
                        var val = f.GetValue(this);
                        pro.SetValue(serializer, val);
                    }
                }
            }
            return serializer;
        }

        public virtual void DeserializeFrom(DrawGeometrySerializerBase serializer)
        {
            Pen = new Pen(new SolidColorBrush(serializer.Color), serializer.StrokeThickness);
            if (serializer.IsDashStyle)
            {
                Pen.DashStyle = DashStyle;
            }
            geometry = Geometry.Parse(serializer.Geometry).GetFlattenedPathGeometry();
            geometry.Transform = new TranslateTransform(serializer.Matrix.OffsetX, serializer.Matrix.OffsetY);
            Index = serializer.Index;
            ShowIndex = serializer.ShowIndex;
            Text = serializer.Text;
            beginingPoint = serializer.BeginingPoint;
            var serializerClassDescribe = ClassDescribe.Get(serializer.GetType());
            var serializerProperties = serializerClassDescribe.PropertyDescribes.Where(p => p.PropertyInfo.DeclaringType == serializer.GetType()).Select(p => p.PropertyInfo).ToList();
            foreach (var pro in serializerProperties)
            {
                var p = this.GetType().GetProperty(pro.Name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);

                if (p != null && p.CanWrite)
                {
                    var val = pro.GetValue(serializer);
                    p.SetValue(this, val);
                }
                else
                {
                    var fieldName = pro.Name[0].ToString().ToLower() + pro.Name.Substring(1);
                    var flag = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic;
                    var f = this.GetType().GetField(fieldName, flag);
                    if (f == null)
                    {
                        f = this.GetType().GetField("_" + fieldName, flag);
                    }
                    if (f != null)
                    {
                        var val = pro.GetValue(serializer);
                        f.SetValue(this, val);
                    }
                }
            }
            IsFinish = true;
            Draw();
        }

        #endregion 序列化

        #region 属性

        private Point beginingPoint = new Point();

        /// <summary>
        /// 左上角最小点
        /// </summary>
        protected virtual Point BeginingPoint
        {
            get
            {
                if (beginingPoint.X != 0 || beginingPoint.Y != 0) return beginingPoint;
                if (pathGeometry != null && pathGeometry.Figures.Count > 0)
                {
                    var ps = GetPoints();
                    var minX = ps.Min(p => p.X);
                    beginingPoint = ps.FirstOrDefault(p => p.X == minX);
                }
                return beginingPoint;
            }
        }

        public Int32 TouchId { get; protected set; }

        public Boolean CanTouchEnter { get; protected set; }

        public Boolean CanTouchLeave { get; protected set; }

        public Boolean CanTouchDown { get; protected set; }

        public Boolean CanTouchMove { get; protected set; }

        public Boolean CanTouchUp { get; protected set; }

        public Boolean CanKeyDown { get; protected set; }

        public Boolean CanKeyUp { get; protected set; }

        public Boolean IsFinish { get; internal set; }

        public DrawToolType DrawingToolType { get; protected set; }

        public Boolean CanEdit { get; protected set; }

        public Int32 Mode { get; protected set; }

        protected double FontSize => this.drawingCanvas.FontSize;

        public static bool IsStroked { get; set; } = true;

        public static DashStyle DashStyle => new DashStyle(new double[] { 0, 4 }, 0);

        public bool HasDelete { get; set; } = false;

        Pen pen = null;
        public virtual Pen Pen
        {
            get
            {
                if (pen == null) pen = drawingCanvas.Pen;
                return pen;
            }
            private set => pen = value;
        }

        protected Dpi Dpi => DpiHelper.GetDpiFromVisual(drawingCanvas);

        #endregion 属性

        #region 字段

        protected DrawingCanvas drawingCanvas;
        protected Geometry geometry;
        protected PathGeometry pathGeometry => (PathGeometry)geometry;


        protected Rect selectRect;

        /// <summary>
        /// 序号
        /// </summary>
        public virtual int Index
        {
            get;
            set;
        }
        /// <summary>
        /// 是否显示序号
        /// </summary>
        public bool ShowIndex { get; set; } = true;

        /// <summary>
        /// 文本
        /// </summary>
        public virtual string Text { get; set; }

        #endregion 字段

        protected IEnumerable<Point> GetPoints()
        {
            List<Point> ps = new List<Point>();
            if (pathGeometry != null && pathGeometry.Figures.Count > 0)
            {
                foreach (var fi in pathGeometry.Figures)
                {
                    if (fi is PathFigure pf)
                    {
                        ps.Add(pf.StartPoint);
                    }
                    foreach (var sg in fi.Segments)
                    {
                        if (sg is LineSegment ls)
                        {
                            ps.Add(ls.Point);
                        }
                        if (sg is BezierSegment bs)
                        {
                            ps.AddRange(new List<Point> { bs.Point1, bs.Point2, bs.Point3 });
                        }
                        if (sg is ArcSegment ase)
                        {
                            ps.Add(ase.Point);
                        }
                    }
                }
            }
            return ps;
        }

        protected FormattedText GetFormattedText(string txt = null)
        {
            return drawingCanvas.GetFormattedText(txt ?? Text);
        }

        protected Pen TextPen => new Pen(Pen.Brush, 1);

        /// <summary>
        /// 加入序号
        /// </summary>
        private void DrawIndex(DrawingContext dc)
        {
            if (!this.ShowIndex)
            {
                return;
            }
            if (Index > 0 && DrawerConfig.Instance.ShowDrawIndex)
            {
                var dx = Index.ToString().Length * FontSize;
                var textPoint = new Point(BeginingPoint.X - dx, BeginingPoint.Y - FontSize / 2 - 2);
                var formattedText = drawingCanvas.GetFormattedText(Index.ToString());
                var translate = GetTranslateTransform();
                if (translate != null)
                {
                    textPoint.X += translate.X;
                    textPoint.Y += translate.Y;
                }
                dc.DrawText(formattedText, textPoint);
            }
        }

        protected TranslateTransform GetTranslateTransform()
        {
            if (geometry.Transform is TranslateTransform tt) return tt;
            return null;
        }
    }
}